#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>
#include <vector>
#include <thread>
#include <pthread.h>
#include "LockGuard.hpp"
using namespace std;
#define NUM 3

__thread int g_val = 0; //全局变量

pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER; //全局互斥锁，不需要destroy

struct ThreadData{
public:
    ThreadData(const string& name) :_name(name)
    {}

    string _name;
};

class threadData
{
public:
    threadData(int number,pthread_mutex_t *_mutex)
        :mutex(_mutex)
    {
        threadname = "thread-" + to_string(number);
    }

public:
    pthread_mutex_t *mutex; //互斥锁
    string threadname;
};

int tickets = 100; // 用多线程，模拟一轮抢票

// __thread ThreadData d1("thread_local_data"); //线程局部存储的全局变量

string toHex(pthread_t tid)
{
    char buf[128];
    snprintf(buf,sizeof(buf),"0x%x",tid);
    return string(buf);
}

void* threadFunc(void* arg)
{
    pthread_detach(pthread_self()); //分离线程，线程结束后自动回收资源

    ThreadData *data = reinterpret_cast<ThreadData *>(arg); //将void*转换为ThreadData*
    int i = 0;
    pid_t pid = getpid();
    string tid= toHex(pthread_self());
     while (i < 3)
     {
          cout << "pid: " << pid << ", tid : " << tid
               << ", threadname: " << data->_name
               << ", g_val: " << g_val << " ,&g_val: " << &g_val <<endl;
         sleep(1);
         i++;
     }
    delete data; //释放堆区内存
    return nullptr;
}

void *getTicket(void *args)
{
    threadData *td = static_cast<threadData *>(args);
    const char *name = td->threadname.c_str();
    while (true)
    {
        {   //划分一块临界区，使用LockGuard自动加锁和解锁，采用RAII机制
            LockGuard lock(td->mutex);
            if(tickets > 0)
            {
                usleep(1000);
                printf("who=%s, get a ticket: %d\n", name, tickets); // ?
                tickets--;
            }
            else{
                break;
            }
        }
        usleep(1000); //模拟抢票的延时?
    }
//    while (true)
//    {
//        pthread_mutex_lock(td->mutex); // 加锁
//        if(tickets > 0)
//        {
//            usleep(1000);
//            printf("who=%s, get a ticket: %d\n", name, tickets); // ?
//            tickets--;
//            pthread_mutex_unlock(td->mutex); // 解锁
//        }
//        else{
//            pthread_mutex_unlock(td->mutex); // 解锁
//            break;
//        }
//        usleep(1000); //模拟抢票的延时?
//    }
    printf("%s ... quit\n", name);
    return nullptr;
}

void test1()
{
    //来创建多个线程实验！
    vector<pthread_t> threads;
    for(int i=0;i<NUM;i++)
    {
        string tmp = "thread" + to_string(i);
//        ThreadData data(tmp);//不能够直接创建，因为ThreadData在主栈区创建，出作用域后销毁！
        auto data = new ThreadData(tmp); //使用new在堆区创建，线程结束后不会销毁
        pthread_t tid;
        pthread_create(&tid,nullptr,threadFunc,(void*)data);
        threads.push_back(tid);
        sleep(1);
    }
    sleep(1);
    // for(int i=0;i<NUM;i++)
    // {
    //     pthread_detach(threads[i]); //分离线程,就不能等待线程结束了
    // }
    for(int i=0;i<NUM;i++)
    {
        pthread_join(threads[i],nullptr); //等待线程结束
    }
    cout<< "test1 end" << endl;
}


void test2()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr); // 初始化互斥锁
    vector<pthread_t> tids;
    vector<threadData *> thread_datas;
    for (int i = 1; i <= NUM; i++)
    {
        pthread_t tid;
        threadData *td = new threadData(i, &mutex); // 创建线程数据对象
        thread_datas.push_back(td);
        pthread_create(&tid, nullptr, getTicket, thread_datas[i - 1]);
        tids.push_back(tid);
    }
    // thread-2 ... quit
    // who=thread-1, get a ticket: 0
    // thread-1 ... quit
    // who=thread-3, get a ticket: -1
    // thread-3 ... quit
    //为什么会这样？发生了数据不一致的问题
    // 一、问题根源：多线程对tickets的非原子操作
    // tickets--实际包含三个 CPU 指令：
    // LOAD：从内存读取tickets的值到 CPU 寄存器；SUB：在寄存器中执行tickets = tickets - 1；STORE：将寄存器的值写回内存。
    // 当多个线程并发执行这三个步骤时，由于指令交错，会导致数据不一致。

    // 二、从 CPU 缓存角度分析(同时判断，但未同步--)
    // 现代 CPU 为提高效率，会将内存数据缓存到各级缓存（L1/L2/L3）或寄存器中。假设初始tickets=1，可能发生以下情况：
    // 线程 A 和线程 B 同时读取缓存
    // 线程 A 和 B 同时进入if(tickets > 0)判断，由于 CPU 缓存机制，它们可能同时从缓存读取到tickets=1（此时内存中tickets仍为 1）。
    // 关键点：线程 A 和 B 的 CPU 缓存未同步，都认为tickets=1，因此都通过了if判断。
    // 线程 A 和线程 B 交错执行减法
    // 线程 A 执行SUB和STORE，将tickets减为 0 并写回内存；
    // 线程 B 此时仍使用自己缓存中的tickets=1（未感知到内存已更新），继续执行SUB和STORE，将tickets从 1 减为 0 后写回内存；
    // 结果：两次减 1 操作后，tickets本应变为 - 1，但实际被错误地写为 0。
    // 线程继续重复上述步骤
    // 当tickets=0时，仍可能有线程从缓存读取到旧值（如 1 或 0），继续执行减法，导致tickets变为负数（如 - 1、-2）。

    // 三、从内存可见性角度分析
    // 线程间的内存可见性问题由内存模型决定：
    // 每个线程可能在 CPU 缓存或寄存器中保留变量副本；
    // 对变量的修改不会立即刷新到主内存，其他线程也不会立即感知到变化。
    // 在你的代码中：
    // 线程 A 修改tickets后，未及时通知其他线程刷新缓存；
    // 线程 B 继续使用自己缓存中的旧值，导致重复操作。
    
    // 四、从原子性角度分析
    // if(tickets > 0)和tickets--这两个操作不是原子的：
    // 线程 A 在判断if(tickets > 0)后，可能被线程 B 打断；
    // 线程 B 此时可能将tickets减为 0 甚至负数；
    // 线程 A 恢复执行后，由于已通过判断，仍会执行tickets--，导致出现负数。

    for (auto thread : tids)
    {
        pthread_join(thread, nullptr);
    }

    pthread_mutex_destroy(&mutex); // 销毁互斥锁

    for (auto td : thread_datas)
    {
        delete td;
    }
}

int main()
{
    test2();
    return 0;
}